package org.chaiyi.template.core.framework.mongo;

import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.SneakyThrows;
import org.chaiyi.template.core.common.repository.MongoEntity;
import org.chaiyi.template.core.common.supporter.MongoSupporter;
import org.chaiyi.template.core.framework.support.SupporterCache;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.function.Function;

/**
 * 基于MongoDB的模板仓库
 */
@Lazy
public class Repository<T extends MongoSupporter, D extends MongoEntity> {
    private final MongoManager mongoManager;  // MongoDB管理器
    private final SupporterCache<T> supporterCache;  // 支持者缓存
    private final Class<D> entityClass;

    private final D eEntity;

    /**
     * 构造方法
     */
    @SneakyThrows
    public Repository(Class<T> supporter, Class<D> entity, SupporterCache<T> supporterCache, MongoManager mongoManager) {
        this.mongoManager = mongoManager;
        this.supporterCache = supporterCache;

        entityClass = entity;

        eEntity = entityClass.getConstructor().newInstance();
    }


    /**
     * 创建
     */
    public Mono<D> create(String tenantId, String buCode, D entity) {
        return opsMono(tenantId, buCode, ops -> ops.insert(entity, getCollectionName(tenantId, buCode)));
    }

//    /**
//     * 创建
//     */
//    public Mono<D> create(String tenantId, String buCode, Object entity) {
//        return opsMono(tenantId, buCode, ops -> ops.insert((D)entity, eEntity.collectionName()));
//    }

    /**
     * 批量创建
     */
    public Flux<D> batchCreate(String tenantId, String buCode, Mono<Collection<D>> list) {
        return opsFlux(tenantId, buCode, ops -> ops.insertAll(list, getCollectionName(tenantId, buCode)));
    }

    /**
     * 通过ID查找
     */
    public Mono<D> findItem(String tenantId, String buCode, String id) {
        return opsMono(tenantId, buCode, ops -> ops.findById(id, entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 根据主键ID删除
     */
    public Mono<D> deleteById(String tenantId, String buCode, String id) {
        return opsMono(tenantId, buCode, ops -> ops.findAndRemove(
                Query.query(Criteria.where("id").is(id))
                , entityClass, getCollectionName(tenantId, buCode)));
    }

    public Mono<DeleteResult> deleteAll(String tenantId, String buCode, Query query) {
        return opsMono(tenantId, buCode, ops -> ops.remove(query, entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 根据查询条件查找
     */
    public Mono<D> findOne(String tenantId, String buCode, Query query) {
        return opsMono(tenantId, buCode, ops -> ops.findOne(query, entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 根据ID 全量更新
     */
    public Mono<D> save(String tenantId, String buCode, D entity) {
        return opsMono(tenantId, buCode, ops -> ops.save(entity,  getCollectionName(tenantId, buCode)));
    }
    /**
     * 更新一条
     */
    public Mono<UpdateResult> updateOne(String tenantId, String buCode, Query query, Update update) {
        return opsMono(tenantId, buCode,
                ops -> ops.updateFirst(query, update, entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 批量更新
     */
    public Mono<UpdateResult> updateMulti(String tenantId, String buCode, Query query, Update update) {
        return opsMono(tenantId, buCode,
                ops -> ops.updateMulti(query, update, entityClass, getCollectionName(tenantId, buCode)));
    }


    /**
     * 获取数据总数量
     */
    public Mono<Long> count(String tenantId, String buCode, Query query) {
        return opsMono(tenantId, buCode, ops -> ops.count(query, entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 获取列表
     */
    public Flux<D> findList(String tenantId, String buCode, Query query) {
        return opsFlux(tenantId, buCode, ops -> ops.find(query, entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 聚合查询
     */
    public <V> Flux<V> aggregate(String tenantId, String buCode, Aggregation aggregation, Class<V> outClass) {
        return opsFlux(tenantId, buCode, ops -> ops.aggregate(aggregation, getCollectionName(tenantId, buCode), outClass));
    }

    /**
     * 获取所有数据
     */
    public Flux<D> findList(String tenantId, String buCode) {
        return opsFlux(tenantId, buCode, ops -> ops.findAll(entityClass, getCollectionName(tenantId, buCode)));
    }

    /**
     * 获取操作MongoDB的Mono对象
     *
     * @param tenantId 租户ID
     * @param buCode   业务单元代码
     * @param function MongoDB操作函数
     * @return MongoDB操作结果的Mono对象
     */
    private <V> Mono<V> opsMono(String tenantId, String buCode, Function<ReactiveMongoOperations, Mono<V>> function) {
        return mongoManager.getOperations((Mono<MongoSupporter>) supporterCache.getSupporter(tenantId, buCode))
                .flatMap(function);
    }


    /**
     * 获取操作MongoDB的Flux对象
     *
     * @param tenantId 租户ID
     * @param buCode   业务单元代码
     * @param function MongoDB操作函数
     * @return MongoDB操作结果的Flux对象
     */
    private <V> Flux<V> opsFlux(String tenantId, String buCode, Function<ReactiveMongoOperations, Flux<V>> function) {
        return mongoManager.getOperations((Mono<MongoSupporter>) supporterCache.getSupporter(tenantId, buCode))
                .flatMapMany(function);
    }


    private String getCollectionName(String tenantId, String buCode) {
        return tenantId + "_" + buCode + "_" + eEntity.collectionName();
    }

}
